Crate extend

source ·
Expand description

Create extensions for types you don’t own with extension traits but without the boilerplate.

Example:

use extend::ext;

#[ext]
impl<T: Ord> Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

assert_eq!(
    vec![1, 2, 3],
    vec![2, 3, 1].sorted(),
);

How does it work?

Under the hood it generates a trait with methods in your impl and implements those for the type you specify. The code shown above expands roughly to:

trait VecExt<T: Ord> {
    fn sorted(self) -> Self;
}

impl<T: Ord> VecExt<T> for Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

Supported items

Extensions can contain methods or associated constants:

use extend::ext;

#[ext]
impl String {
    const CONSTANT: &'static str = "FOO";

    fn method() {
        // ...
    }
}

Configuration

You can configure:

  • The visibility of the trait. Use pub impl ... to generate pub trait .... The default visibility is private.
  • The name of the generated extension trait. Example: #[ext(name = MyExt)]. By default we generate a name based on what you extend.
  • Which supertraits the generated extension trait should have. Default is no supertraits. Example: #[ext(supertraits = Default + Clone)].

More examples:

use extend::ext;

#[ext(name = SortedVecExt)]
impl<T: Ord> Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

#[ext]
pub(crate) impl i32 {
    fn double(self) -> i32 {
        self * 2
    }
}

#[ext(name = ResultSafeUnwrapExt)]
pub impl<T> Result<T, std::convert::Infallible> {
    fn safe_unwrap(self) -> T {
        match self {
            Ok(t) => t,
            Err(_) => unreachable!(),
        }
    }
}

#[ext(supertraits = Default + Clone)]
impl String {
    fn my_length(self) -> usize {
        self.len()
    }
}

For backwards compatibility you can also declare the visibility as the first argument to #[ext]:

use extend::ext;

#[ext(pub)]
impl i32 {
    fn double(self) -> i32 {
        self * 2
    }
}

async-trait compatibility

Async extensions are supported via async-trait.

Be aware that you need to add #[async_trait] below #[ext]. Otherwise the ext macro cannot see the #[async_trait] attribute and pass it along in the generated code.

Example:

use extend::ext;
use async_trait::async_trait;

#[ext]
#[async_trait]
impl String {
    async fn read_file() -> String {
        // ...
    }
}

Other attributes

Other attributes provided below #[ext] will be passed along to both the generated trait and the implementation. See async-trait compatibility above for an example.

Attribute Macros

  • See crate docs for more info.
  • Like ext but always add Sized as a supertrait.